package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.config.HighlightResultMapper;
import cn.itsource.ymcc.domain.CourseDoc;
import cn.itsource.ymcc.repository.CourseElasticsearchRepository;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.result.PageList;
import cn.itsource.ymcc.service.ICourseESService;
import cn.itsource.ymcc.vo.AggrPageList;
import cn.itsource.ymcc.vo.AggrTermsBucket;
import cn.itsource.ymcc.vo.CourseQueryVo;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class CourseESServiceImpl implements ICourseESService {
    @Autowired
    private CourseElasticsearchRepository courseElasticsearchRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;//高亮和聚合都要用这个
    @Autowired
    private HighlightResultMapper highlightResultMapper;
    /**
     * 分页查询 es
     * @param courseQueryVo
     * @return
     */
    @Override
    public JsonResult query(CourseQueryVo courseQueryVo) {
        //第一层 query
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();//query
        BoolQueryBuilder bool = QueryBuilders.boolQuery();//bool

        //高级查询底层都是通过单分支语句动态拼接条件
        if(StringUtils.hasText(courseQueryVo.getKeyword())) {
            bool.filter().add(QueryBuilders.matchQuery("name", courseQueryVo.getKeyword()));
        }
        //高级查询底层都是通过单分支语句动态拼接条件
        if(StringUtils.hasText(courseQueryVo.getGradeName())) {
            bool.filter().add(QueryBuilders.matchQuery("gradeName", courseQueryVo.getGradeName()));
        }
        if(courseQueryVo.getPriceMin() != null){
            bool.filter().add(QueryBuilders.rangeQuery("price").gte(courseQueryVo.getPriceMin()));
        }
        if(courseQueryVo.getPriceMax() != null){
            bool.filter().add(QueryBuilders.rangeQuery("price").lte(courseQueryVo.getPriceMax()));
        }
        queryBuilder.withQuery(bool);

        //分页  注意：SpringDataElasticsearch的当前页码是从0开始的
        Pageable pageable = PageRequest.of(courseQueryVo.getPageNo()-1, courseQueryVo.getPageSize());
        queryBuilder.withPageable(pageable);//添加分页的信息

        if(StringUtils.hasText(courseQueryVo.getOrderField())){
            SortOrder sortOrder = SortOrder.ASC;
            if("desc".equalsIgnoreCase(courseQueryVo.getOrderType())){
                sortOrder = SortOrder.DESC;
            }
            SortBuilder sortBuilder = SortBuilders.fieldSort(courseQueryVo.getOrderField()).order(sortOrder);
            queryBuilder.withSort(sortBuilder);
        }

        //聚合查询,按照机构名字聚合，安装数量倒排
        queryBuilder.addAggregation(AggregationBuilders
                .terms("gradeNameAgg") //聚合名字
                .field("gradeName")	//聚合的字段
                .order(BucketOrder.count(false)).size(200));//排序后取前20条


        //高亮设置
        HighlightBuilder.Field  highlightField = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red'>")
                .postTags("</span>");

        queryBuilder.withHighlightFields(highlightField);


        AggregatedPage<CourseDoc> page =
                elasticsearchRestTemplate.queryForPage(queryBuilder.build(), CourseDoc.class, highlightResultMapper);//执行上面的查询
        System.out.println("当前页码："+page.getNumber());
        System.out.println("每页条数："+page.getSize());
        System.out.println("总条数："+page.getTotalElements());
        System.out.println("总页数："+page.getTotalPages());
        System.out.println("当前页条数："+page.getNumberOfElements());


        Map<String, List<AggrTermsBucket>> aggrResult = getAggrResult(page);

        //封装返回数据
        List<CourseDoc> content = page.getContent();
        //PageList<CourseDoc> pageList = new PageList<>(page.getTotalElements(),content);
        AggrPageList<CourseDoc> pageList = new AggrPageList<CourseDoc>(page.getTotalElements(),content,aggrResult);
        return JsonResult.success().setData(pageList);
    }

    /**
     * 构建聚合对象
     * @param page
     * @return
     */
    private Map<String, List<AggrTermsBucket>> getAggrResult(AggregatedPage<CourseDoc> page) {
        //用来封装聚合结果
        Map<String,List<AggrTermsBucket>> aggrMap = new HashMap<>();

        //遍历聚合结果
        Aggregations aggregations = page.getAggregations();

        Map<String, Aggregation> stringAggregationMap = aggregations.asMap();

        Set<Map.Entry<String, Aggregation>> entries = stringAggregationMap.entrySet();

        entries.forEach(e->{
            //集合的名字
            String key = e.getKey();
            //集合的值的处理
            ParsedStringTerms value = (ParsedStringTerms)e.getValue();
            List<? extends Terms.Bucket> buckets = value.getBuckets();

            //使用lomda 把  List<StringTerms.Bucket>  转成  List<AggrTermsVo>
            List<AggrTermsBucket> aggrTermsVos = buckets.stream().map(bucket -> {
                return new AggrTermsBucket(bucket.getKeyAsString(), bucket.getDocCount());
            }).collect(Collectors.toList());

            aggrMap.put(key, aggrTermsVos);
        });
        return aggrMap;
    }

}
